<template>
  <div class="custom-modal-wrap" ref="modalWrapRef">
    <div class="custom-modal">
      <div class="title">
        <span>绘制可点击区域</span>
        <div class="close-button" @click="onClose">
          <CloseOutlined />
        </div>
      </div>
      <div class="content-wrap">
        <div class="content">
          <div class="img-container-wrap" ref="containerWrapRef">
            <canvas class="container" @mousemove="onCanvasHover"></canvas>
            <div class="container-mask" ref="containerMaskRef"></div>
            <div class="canvas-tools" v-if="canvasInstance && !canvasInstance.readonly">
              <a-button @click="toggleDrawType">
                {{
                  canvasInstance.createType && canvasInstance.createType == 2
                    ? "退出绘制"
                    : "开始绘制"
                }}
              </a-button>
              <a-button style="margin-left: 10px" @click="canvasFitZoom">
                还原图片大小
              </a-button>
            </div>
            <div
              class="bind-wrap"
              ref="bindWrapRef"
              v-if="bindPolyBuildVisible"
            >
              <div style="display: flex; align-items: center">
                <a-select
                  v-model:value="currentDrawPoly.bindType"
                  style="width: 60px"
                  size="small"
                  placeholder="请选择"
                  :getPopupContainer="getPopupContainer"
                  @change="onBindTypeChange"
                >
                  <a-select-option
                    v-for="item of bindTypeList"
                    :key="item.label"
                    :value="item.value"
                    >{{ item.label }}</a-select-option
                  >
                </a-select>
                <template v-if="currentDrawPoly.bindType == 'build'">
                  <a-select
                    v-model:value="currentDrawPoly.build_id"
                    style="width: 150px; margin-left: 5px"
                    size="small"
                    placeholder="请选择"
                    :getPopupContainer="getPopupContainer"
                    @change="onBindBuildChange"
                  >
                    <a-select-option
                      v-for="item of bindBuildList"
                      :key="item.build_name"
                      :value="item.id"
                      >{{ item.build_name }}</a-select-option
                    >
                  </a-select>
                </template>
                <template v-if="currentDrawPoly.bindType == 'monitor'">
                  <a-select
                    v-model:value="currentDrawPoly.device_id"
                    style="width: 150px; margin-left: 5px"
                    size="small"
                    placeholder="请选择监控设备"
                    @change="onBindMonitorChange"
                    :getPopupContainer="getPopupContainer"
                  >
                    <a-select-option
                      v-for="item of binMonitorList"
                      :key="item.id"
                      :value="item.id"
                      >{{ item.name }}</a-select-option
                    >
                  </a-select>
                </template>
                <a-button
                  type="primary"
                  size="small"
                  style="margin-left: 5px"
                  @click="bindPolySave"
                >
                  确定
                </a-button>
              </div>
            </div>
            <div class="label-wrap" ref="labelWrapRef">
              <div class="label">
                {{ currentPolyName }}
              </div>
            </div>
          </div>
        </div>

        <div class="button-list-wrap">
          <div style="display: flex; align-items: center" v-if="canvasInstance && !canvasInstance.readonly">
            <a-button type="primary" class="button" @click="saveDraw">
              保存
            </a-button>
          </div>
          <div
            style="display: flex; align-items: center"
            v-else-if="isBindMode"
          >
            <a-button type="primary" class="button" @click="saveBind">
              保存
            </a-button>
            <div style="margin-left: 15px; color: #fff">
              *标记只能标记在已绘制的区域上
            </div>
          </div>
          <div class="button-list" v-else>
            <a-button type="primary" class="button" @click="startDraw">
              绘制
            </a-button>
            <a-button
              type="primary"
              class="button"
              style="margin-top: 10px"
              @click="startBind"
              >标记</a-button
            >
            <a-button
              type="primary"
              class="button"
              style="margin-top: 10px"
              @click="showSettingModal"
              >设置</a-button
            >
          </div>
        </div>

        <div class="instructions" v-if="canvasInstance && !canvasInstance.readonly" @click="showInstructions">
          查看操作说明
        </div>
      </div>

      <!-- 操作说明 -->
      <a-modal
        v-model:visible="instructionsVisible"
        title="操作说明"
        :footer="null"
        centered
        @cancel="instructionsVisible = false"
      >
        <p>
          1.
          创建多边形：鼠标左键单击添加点，双击闭合完成创建，Esc退出创建，Backspace
          退一步删除选择点。
        </p>
        <p>2. 拖动：按住鼠标右键拖动画布。</p>
        <p>3. 缩放：鼠标滚轮缩放画布</p>
        <p>4. 删除：选中形状，Backspace删除</p>
      </a-modal>

      <!-- 设置 -->
      <a-modal
        v-model:visible="settingVisible"
        title="设置"
        centered
        @ok="saveSetting"
      >
        <div style="display: flex; align-items: center">
          <div>点击楼宇类型区域的动作</div>
          <a-select
            ref="select"
            v-model:value="operationCurrent"
            style="width: 120px; margin-left: 10px"
            :getPopupContainer="getPopupContainer"
          >
            <a-select-option
              v-for="item of operationList"
              :key="item.label"
              :value="item.value"
              >{{ item.label }}</a-select-option
            >
          </a-select>
        </div>
        <div style="display: flex; align-items: center; margin-top: 30px">
          <div>鼠标停留标注点</div>
          <a-select
            ref="select"
            v-model:value="isHoverShowLabel"
            style="width: 120px; margin-left: 10px"
            :getPopupContainer="getPopupContainer"
          >
            <a-select-option :value="1">展示</a-select-option>
            <a-select-option :value="0">不展示</a-select-option>
          </a-select>
          <div style="margin-left: 5px">名称</div>
        </div>
      </a-modal>
    </div>
  </div>
</template>

<script>
import {
  defineComponent,
  reactive,
  toRefs,
  computed,
  watch,
  toRaw,
  ref,
  nextTick,
} from "vue";
import request from "../../../../common/utils/request";
import store from "../../../../store/index";
import { useRoute, useRouter } from "vue-router";
import utils from "../../../../common/utils/utils";
import { Modal, notification, message } from "ant-design-vue";
import {
  LoadingOutlined,
  CheckCircleOutlined,
  CloseOutlined,
} from "@ant-design/icons-vue";
import CanvasSelect from "canvas-select";

let canvasHoverTimer = null;

export default defineComponent({
  name: "DrawClickableArea",
  components: { LoadingOutlined, CheckCircleOutlined, CloseOutlined },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    bgImgUrl: {
      type: String,
      default: "",
    },
    id: {
      type: [String, Number],
      default: 0,
    },
  },

  setup(props, context) {
    const route = useRoute();
    const router = useRouter();
    const state = reactive({
      canvasInstance: null,
      instructionsVisible: false, //操作说明弹窗
      containerMaskRef: null,
      bindWrapRef: null,
      labelWrapRef: null,
      bindPolyBuildVisible: false, //绑定楼宇pop是否显示

      isBindMode: false, //是否是标记模式

      settingVisible: false, //设置弹窗
      isHoverShowLabel: 1, //鼠标停留是否展示名称
      operationList: [], //点击楼宇类型区域的动作-下拉列表
      operationCurrent: null,

      modalWrapRef: null,

      bindTypeList: [
        {
          label: "楼宇",
          value: "build",
        },
        {
          label: "监控",
          value: "monitor",
        },
      ], //建筑类型
      bindBuildList: [], //建筑列表
      currentDrawPoly: {}, //选中的绘制图形
      drawPolyData: [], //绘制图形数据
      defaultOptions: [],

      currentPolyName: "",

      binMonitorList: [], //监控设备列表
    });

    const onClose = () => {
      context.emit("onClose");
    };

    const initCanvasInstance = (url) => {
      state.canvasInstance = new CanvasSelect(".container", url);
      // 加载数据
      state.canvasInstance.setData(state.defaultOptions);
      // 0 不创建(默认)，1创建矩形，2创建多边形，3点标注，4折线标注，5圆形标注
      state.canvasInstance.createType = 0;
      state.canvasInstance.fillStyle = "rgba(0, 0, 0, 0.45)";
      state.canvasInstance.strokeStyle = "#2681f3";
      state.canvasInstance.readonly = true;

      state.canvasInstance.on("select", (info) => {
        console.log("select", info);
        // 可对选中对参数info进行修改
        // 修改标签：info.label="hello"
        // 单个形状修改填充颜色：info.fillStyle="#0f0"
        // 然后调用instance.update()更新视图

        // 编辑模式下，生成dom，给图案绑定楼宇
        if (state.isBindMode) {
          // 绑定时隐藏label
          hidePolyLabel();
          if (info) {
            state.drawPolyData.forEach((poly) => {
              if (poly.uuid == info.uuid) {
                state.currentDrawPoly = info;
                //默认标注类型楼宇
                if (!state.currentDrawPoly.bindType) {
                  state.currentDrawPoly.bindType = "build";
                }
                poly.active = true;
                poly.fillStyle = "rgba(41, 150, 255, 0.5)";
                poly.lineWidth = 3;
              } else {
                poly.active = false;
                poly.fillStyle = "transparent";
                poly.lineWidth = 1;
              }
            });
            showBindPolyBuild();
          } else {
            hideBindPolyBuild();
            state.drawPolyData.forEach((poly) => {
              poly.active = false;
              poly.fillStyle = "transparent";
              poly.lineWidth = 1;
            });
          }
        }
      });

      state.canvasInstance.on("updated", (result) => {
        state.drawPolyData = [...result];
      });
    };

    // 给当前图案绑定楼宇
    const showBindPolyBuild = () => {
      let centerX = state.canvasInstance.mouse[0];
      let centerY = state.canvasInstance.mouse[1];
      let transform = "translate(-50%, -50%)";
      // 获取图片右下角坐标位置
      let picTopRight = [
        state.canvasInstance.originX + state.canvasInstance.IMAGE_WIDTH,
        state.canvasInstance.IMAGE_HEIGHT,
      ];
      if (centerX + 280 >= picTopRight[0]) {
        if (centerY < 240) {
          //如果点击位置离右上角太近，移动到鼠标左下方显示
          transform = "translate(-100%, 0)";
        } else {
          //如果点击位置离右下角太近，移动到鼠标左上方显示
          transform = "translate(-100%, -100%)";
        }
      }

      state.bindPolyBuildVisible = true;
      nextTick(() => {
        state.bindWrapRef.style.top = `${centerY}px`;
        state.bindWrapRef.style.left = `${centerX}px`;
        state.bindWrapRef.style.transform = transform;
      });
    };

    // 隐藏绑定气泡
    const hideBindPolyBuild = () => {
      state.bindPolyBuildVisible = false;
      state.currentDrawPoly = {};
      state.drawPolyData.forEach((poly) => {
        poly.active = false;
        poly.fillStyle = "transparent";
        poly.lineWidth = 1;
      });
      state.canvasInstance.update();
    };

    // 获取多边形中心点位置
    const getPolyCenter = (poly) => {
      let xArr = [];
      let yArr = [];
      poly.coor.forEach((item) => {
        xArr.push(item[0]);
        yArr.push(item[1]);
      });
      xArr = xArr.sort((a, b) => {
        return a - b;
      });
      yArr = yArr.sort((a, b) => {
        return a - b;
      });
      let centerX = (xArr[0] + xArr[xArr.length - 1]) / 2;
      let centerY = (yArr[0] + yArr[yArr.length - 1]) / 2;
      return { centerX, centerY };
    };

    const startDraw = () => {
      //打开绘制模式
      state.canvasInstance.readonly = false;
      //开启画布
      state.canvasInstance.lock = false;
      //遮罩层鼠标事件关闭，放开canvas鼠标事件
      state.containerMaskRef.style.pointerEvents = "none";
      state.canvasInstance.createType = 2;
      state.canvasInstance.ctrlRadius = 3;
      state.canvasInstance.activeStrokeStyle = "#f00";
      // 打开画布缩放
      state.canvasInstance.scrollZoom = true;
    };

    const showInstructions = () => {
      state.instructionsVisible = true;
    };

    // 关闭绘制
    const closeDraw = () => {
      //退出绘制模式
      state.canvasInstance.readonly = true;
      //画布大小复原
      state.canvasInstance.fitZoom();
      //锁定画布
      state.canvasInstance.lock = true;
      //遮罩层鼠标事件开启，屏蔽canvas鼠标事件
      state.containerMaskRef.style.pointerEvents = "all";
      hideBindPolyBuild();
    };

    const saveDraw = () => {
      let params = {
        village_id: props.id,
      };
      for (let key in state.drawPolyData) {
        state.drawPolyData[key].active = false;

        // 过滤掉不完整的多边形（coor.length < 3：图形小于3个点，无法构成完整的多边形）
        if (
          !state.drawPolyData[key].coor ||
          state.drawPolyData[key].coor.length < 3
        ) {
          state.drawPolyData.splice(key, 1);
        }
      }
      state.canvasInstance.setData(state.drawPolyData);
      state.canvasInstance.update();
      if (state.drawPolyData.length > 0) {
        params.draw_info = state.drawPolyData;
      }
      request
        .post("/village", "/project/draw/operate", { ...params })
        .then((res) => {
          message.success("保存成功");
        });
      closeDraw();
    };

    const startBind = () => {
      //打开标记模式
      state.isBindMode = true;
      //开启画布
      state.canvasInstance.lock = false;
      //遮罩层鼠标事件关闭，放开canvas鼠标事件
      state.containerMaskRef.style.pointerEvents = "none";
      state.canvasInstance.createType = -1;
      state.canvasInstance.ctrlRadius = 0;
      state.canvasInstance.activeStrokeStyle = "#FFF";
      // 禁止画布缩放
      state.canvasInstance.scrollZoom = false;
    };

    const saveBind = () => {
      let params = {
        village_id: props.id,
      };
      if (state.drawPolyData.length > 0) {
        state.drawPolyData.forEach((item) => (item.active = false));
        params.draw_info = state.drawPolyData;
      }
      request
        .post("/village", "/project/draw/operate", { ...params })
        .then((res) => {
          message.success("保存成功");
        });

      //退出标记模式
      state.isBindMode = false;
      //画布大小复原
      state.canvasInstance.fitZoom();
      //锁定画布
      state.canvasInstance.lock = true;
      //遮罩层鼠标事件开启，屏蔽canvas鼠标事件
      state.containerMaskRef.style.pointerEvents = "all";
      hideBindPolyBuild();
    };

    const bindPolySave = () => {
      hideBindPolyBuild();
      message.success("标记成功");
    };

    const showSettingModal = () => {
      state.settingVisible = true;
    };

    const saveSetting = () => {
      let params = {
        village_id: props.id,
        draw_info: [],
        setting_info: {},
      };
      if (state.drawPolyData.length > 0) {
        params.draw_info = state.drawPolyData;
      }
      params.setting_info.is_show_mark = state.isHoverShowLabel ? true : false;
      params.setting_info.operation_action = state.operationList.map(
        (item) => ({
          label: item.label,
          value: item.value,
          select: item.value == state.operationCurrent ? 1 : 0,
        })
      );
      request
        .post("/village", "/project/draw/operate", { ...params })
        .then((res) => {
          message.success("保存成功");
        });
      state.settingVisible = false;
    };

    //切换createType
    const toggleDrawType = () => {
      state.canvasInstance.createType =
        state.canvasInstance.createType == 2 ? 0 : 2;
    };

    //画布大小复原
    const canvasFitZoom = () => {
      state.canvasInstance.fitZoom();
    };

    const getBuildList = () => {
      request
        .get("/org", "/build/villageAndBuildList", { checked: 1 })
        .then((res) => {
          let currentVillage = res.villageList.find(
            (item) => item.id == props.id
          );
          state.bindBuildList = currentVillage.buildList;
        });
    };

    const getDrawDetail = () => {
      return new Promise((resolve) => {
        request
          .get("/village", "/project/draw/get", { village_id: props.id })
          .then((res) => {
            if (res.result) {
              state.defaultOptions = res?.result?.draw_info || [];
              if (res.result.setting_info) {
                state.isHoverShowLabel = res.result.setting_info.is_show_mark
                  ? 1
                  : 0;
                state.operationList = res.result.setting_info.operation_action;

                let currentAction = null;
                state.operationList.forEach((item) => {
                  if (item.select) {
                    currentAction = item.value;
                  }
                });
                // 如果没有选中的，默认选择第一个
                if (!currentAction) {
                  currentAction = state.operationList[0].value;
                }
                state.operationCurrent = currentAction;
              }
            }
            resolve();
          });
      })
    };

    const onBindTypeChange = (value, option) => {
      state.currentDrawPoly.build_id = null;
      state.currentDrawPoly.build_name = null;
      state.currentDrawPoly.device_id = null;
      state.currentDrawPoly.device_type = null;
      state.currentDrawPoly.device_name = null;
    };

    const onBindBuildChange = (value, option) => {
      console.log(value, option);
      state.currentDrawPoly.build_name = option.key;
    };

    const onBindMonitorChange = (value, option) => {
      console.log(value, option);
      state.currentDrawPoly.device_type = "monitor";
      state.currentDrawPoly.device_name = option.key;
    };

    // 判断该点是否在多边形内
    const isPointInPolygon = (x, y, poly) => {
      let isInside = false;
      for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
        const xi = poly[i][0],
          yi = poly[i][1];
        const xj = poly[j][0],
          yj = poly[j][1];
        const intersect =
          yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
        if (intersect) {
          isInside = !isInside;
        }
      }
      return isInside;
    };

    const onCanvasHover = () => {
      // 有绘制数据且标记模式下
      if (state.drawPolyData.length > 0 && state.isBindMode) {
        // 加100ms防抖
        if (canvasHoverTimer) {
          clearTimeout(canvasHoverTimer);
          canvasHoverTimer = null;
        }
        if (!state.bindPolyBuildVisible) {
          canvasHoverTimer = setTimeout(() => {
            togglePolyLabel();
          }, 100);
        }
      }
    };

    //鼠标当前hover坐标是否在绘制图形内，如果在则显示label
    const togglePolyLabel = () => {
      hidePolyLabel();
      // 根据画布左上角在窗口上的位置，将鼠标位置换算成在画布上的位置
      let x =
        (state.canvasInstance.mouse[0] - state.canvasInstance.originX) /
        state.canvasInstance.scale;
      let y =
        (state.canvasInstance.mouse[1] - state.canvasInstance.originY) /
        state.canvasInstance.scale;
      let hasInPolygon = false;
      let currentPoly = {};
      state.drawPolyData.forEach((poly) => {
        poly.active = false;
        if (isPointInPolygon(x, y, poly.coor)) {
          hasInPolygon = true;
          currentPoly = poly;
          poly.fillStyle = "rgba(41, 150, 255, 0.5)";
          poly.lineWidth = 3;
        } else {
          poly.fillStyle = "transparent";
          poly.lineWidth = 1;
        }
      });
      if (hasInPolygon) {
        currentPoly.active = true;
        state.currentPolyName =
          currentPoly.build_name || currentPoly.device_name;
        if (state.currentPolyName) {
          showPolyLabel(currentPoly);
        }
      }
      state.canvasInstance.update();
    };

    const showPolyLabel = (poly) => {
      let { centerX, centerY } = getPolyCenter(poly);
      // centerX&centerY 是坐标相当于画布的位置，要转换成在窗口上的位置
      centerX =
        centerX * state.canvasInstance.scale + state.canvasInstance.originX;
      centerY =
        centerY * state.canvasInstance.scale + state.canvasInstance.originY;
      state.labelWrapRef.style.top = `${centerY}px`;
      state.labelWrapRef.style.left = `${centerX}px`;
      state.labelWrapRef.style.display = "block";
    };

    const hidePolyLabel = () => {
      state.currentPolyName = "";
      state.labelWrapRef.style.display = "none";
    };

    const init = () => {
      if (props.bgImgUrl) {
        nextTick(() => {
          getDrawDetail().then(() => {
            initCanvasInstance(props.bgImgUrl);
          })
          getBuildList();
          getMonitorList();
        });
      }
    };

    watch(
      () => props.visible,
      (val) => {
        if (val) {
          nextTick(() => {
            state.modalWrapRef.style.display = "block";
          });
          init();
        } else {
          state.defaultOptions = [];
          if (state.canvasInstance) {
            state.canvasInstance.createType = 0;
            state.canvasInstance.destroy();
            state.canvasInstance = null;
          }
          state.drawPolyData = [];
          state.labelWrapRef.style.display = "none";
          state.modalWrapRef.style.display = "none";
          state.isBindMode = false;
          state.bindPolyBuildVisible = false;
        }
      }
    );

    const getMonitorList = () => {
      let params = {};
      if (state.currentDrawPoly.village_id) {
        params.village_id = state.currentDrawPoly.village_id;
      }
      if (state.currentDrawPoly.build_id) {
        params.build_id = state.currentDrawPoly.build_id;
      }
      request
        .get("/work_iot", "/monitor/equipment/all", { ...params })
        .then((res) => {
          if (res.result) {
            state.binMonitorList = res.result;
          }
        });
    };

    window?.microApp?.removeDomScope(true);

    const getPopupContainer = (trigger) => {
      return window?.rawDocument?.body || document?.body;
    };

    return {
      ...toRefs(state),
      onClose,
      startDraw,
      showInstructions,
      saveDraw,
      startBind,
      saveBind,
      bindPolySave,
      showSettingModal,
      saveSetting,
      toggleDrawType,
      canvasFitZoom,
      onBindTypeChange,
      onBindBuildChange,

      onCanvasHover,

      getMonitorList,
      onBindMonitorChange,
      getPopupContainer,
    };
  },
});
</script>

<style lang="less" scoped>
.custom-modal-wrap {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  background-color: rgba(0, 0, 0, 0.45);
  display: none;

  .custom-modal {
    width: 1200px;
    margin: 100px auto 0 auto;
    border-radius: 8px;
    overflow: hidden;
  }

  .title {
    height: 50px;
    background-color: #fff;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;

    span {
      font-size: 18px;
      font-weight: bold;
    }

    .close-button {
      position: absolute;
      top: 0;
      right: 0;
      width: 50px;
      height: 50px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
    }
  }

  .content-wrap {
    background-color: #09233c;
    padding: 20px;
    position: relative;
    border-radius: 0 0 8px 8px;

    .content {
      width: 800px;
      margin: 50px auto;

      .img-container-wrap {
        width: 800px;
        height: 600px;
        position: relative;

        .container {
          width: 100%;
          height: 100%;
        }

        .container-mask {
          position: absolute;
          width: 100%;
          height: 100%;
          top: 0;
          left: 0;
          z-index: 9;
          box-shadow: inset 0 0 10rem 8rem rgba(0, 0, 0, 0.8);
          //   pointer-events: none;
        }

        .bind-wrap {
          position: absolute;
          z-index: 10;
          transform: translate(-50%, -50%);
        }

        .label-wrap {
          position: absolute;
          z-index: 10;
          display: none;
          transform: translate(-50%, -150%);

          .label {
            background-color: #00a0e9;
            border-radius: 4px;
            padding: 2px 10px;
            font-weight: 600;
            max-width: 180px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            position: relative;
            color: #fff;
          }

          &::after {
            content: "";
            position: absolute;
            width: 0;
            height: 0;
            /* 箭头靠下边 */
            left: calc(50% - 6px);
            bottom: -6px;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 6px solid #00a0e9;
          }
        }
      }

      .canvas-tools {
        margin-top: 10px;
        display: flex;
        justify-content: flex-end;
      }
    }

    .button-list-wrap {
      position: absolute;
      top: 20px;
      left: 20px;

      .button-list {
        display: flex;
        flex-direction: column;
      }

      .button {
        font-size: 13px;
        padding-left: 20px;
        padding-right: 20px;
      }
    }

    .instructions {
      position: absolute;
      top: 20px;
      right: 20px;
      color: #fff;
      cursor: pointer;
    }
  }
}
</style>
